---
title: Combinare stati di provider
---

Assicurarsi di aver letto prima [la sezione Providers](/docs/concepts/providers). 
In questa guida impareremo a combinare gli stati dei provider.

## Combinare stati di provider

Precedentemente abbiamo visto come creare un semplice provider. Ma la realtà è che in molte 
situazioni un provider vorrà leggere lo stato di un altro provider.

Per fare ciò, possiamo usare l'oggetto [ref] passato alla callback del nostro provider e 
usare il metodo [watch].

Come esempio, consideriamo il seguente provider:

```dart
final cityProvider = Provider((ref) => 'London');
```

Possiamo ora creare un altro provider che consumerà `cityProvider`:

```dart
final weatherProvider = FutureProvider((ref) async {
  // Usiamo `ref.watch` per ascoltare un altro provider, e lo passiamo al provider
  // che vogliamo consumare, in questo caso: cityProvider
  final city = ref.watch(cityProvider);

  // Possiamo poi utilizzare il risultato per fare delle operazioni basandoci
  // sul valore di `cityProvider`.
  return fetchWeather(city: city);
});
```

Questo è quanto. Abbiamo creato un provider che dipende da un altro provider.

## Domande frequenti

### Cosa succede se il valore ascoltato cambia nel tempo?

Dipendentemente dal provider che si sta ascoltando, il valore ottenuto potrebbe cambiare 
nel tempo.
Ad esempio, potresti ascoltare un [StateNotifierProvider] oppure il provider che si sta 
ascoltando è stato forzato a riaggiornarsi mediante l'uso di [ProviderContainer.refresh]/[ref.refresh].

Usando [watch], Riverpod è in grado di rilevare che il valore in ascolto è cambiato e 
rieseguirà _automaticamente_ la richiamata (callback) di creazione del provider quando necessario.

Ciò può essere utile per gli stati calcolati (computed states). 
Per esempio, considera uno [StateNotifierProvider] che espone una todo-list:

```dart
class TodoList extends StateNotifier<List<Todo>> {
  TodoList(): super(const []);
}

final todoListProvider = StateNotifierProvider((ref) => TodoList());
```
Un caso d'uso comune sarebbe avere l'UI che filtra la lista dei todo per 
mostrare solo i todo completati/non completati.

Un modo facile per implementare questo tipo di scenario sarebbe:

- creare un [StateProvider] che espone il metodo di filtro attualmente selezionato:

 ```dart
  enum Filter {
    none,
    completed,
    uncompleted,
  }

  final filterProvider = StateProvider((ref) => Filter.none);
  ```
- creare un provider separato che combina il metodo di filtro e la todo-list 
  per esporre la todo-list filtrata:

  ```dart
  final filteredTodoListProvider = Provider<List<Todo>>((ref) {
    final filter = ref.watch(filterProvider);
    final todos = ref.watch(todoListProvider);

    switch (filter) {
      case Filter.none:
        return todos;
      case Filter.completed:
        return todos.where((todo) => todo.completed).toList();
      case Filter.uncompleted:
        return todos.where((todo) => !todo.completed).toList();
    }
  });
  ```

Quindi, la nostra UI può ascoltare `filteredTodoListProvider` per a sua volta ascoltare 
la todo-list filtrata. 
Con questo approccio, l'UI si aggiornerà automaticamente quando o il filtro o la 
todo-list cambia.

Per vedere in azione questo approccio puoi guardare il codice sorgente dell'[esempio Todo List](https://github.com/rrousselGit/riverpod/tree/master/examples/todos).

:::info
Questo comportamento non è specifico di [Provider] e funziona con tutti i tipi provider.

Per esempio, potresti combinare [watch] con [FutureProvider] per implementare una funzione 
di ricerca che supporta instantanei cambi di configurazione:

```dart
// Il filtro di ricerca corrente
final searchProvider = StateProvider((ref) => '');

/// Configurazioni che possono cambiare nel tempo
final configsProvider = StreamProvider<Configuration>(...);

final charactersProvider = FutureProvider<List<Character>>((ref) async {
  final search = ref.watch(searchProvider);
  final configs = await ref.watch(configsProvider.future);
  final response = await dio.get('${configs.host}/characters?search=$search');

  return response.data.map((json) => Character.fromJson(json)).toList();
});
```

Questo codice otterrà una lista di personaggi dal servizio e recupererà automaticamente la 
lista ogni volta che cambiano le configurazioni o quando cambia la query di ricerca.
:::

### Posso leggere un provider senza ascoltarlo?

A volte, si vuole leggere il contenuto di un provider ma senza ricreare il valore esposto 
quando il valore ottenuto cambia.

Un esempio è un `Repository`, il quale legge da un altro provider il token di autenticazione 
dell'utente.
Potremmo usare [watch] e creare un nuovo `Repository` ogni volta che il token dell'utente 
cambia ma servirebbe a poco o niente farlo.
 
In questa situazione, possiamo usare [read], che è simile a [watch] ma non provocherà la 
ricreazione del valore del provider che espone.

In tal caso, una pratica comune è quella di passare `ref.read` all'oggetto creato. L'oggetto 
sarà poi in grado di leggere i provider quando vorrà.

```dart
final userTokenProvider = StateProvider<String>((ref) => null);

final repositoryProvider = Provider((ref) => Repository(ref.read));

class Repository {
  Repository(this.read);

  /// La funzione `ref.read` 
  final Reader read;

  Future<Catalog> fetchCatalog() async {
    String token = read(userTokenProvider);

    final response = await dio.get('/path', queryParameters: {
      'token': token,
    });

    return Catalog.fromJson(response.data);
  }
}
```

:::note NOTA
Puoi anche passare `ref` al posto di `ref.read` al tuo oggetto.

```dart
final repositoryProvider = Provider((ref) => Repository(ref));

class Repository {
  Repository(this.ref);

  final Ref ref;
}
```
L'unica differenza che comporta passare `ref.read` è la scrittura di meno codice, inoltre,
garantisce che il nostro oggetto non utilizzi mai `ref.watch`.
:::

:::danger **NON** usare [read] dentro al body di un provider

```dart
final myProvider = Provider((ref) {
  // É cattiva pratica chiamare `read` qui.
  final value = ref.read(anotherProvider);
});
```
Se hai utilizzato [read] come tentativo di evitare rebuilds non volute del tuo oggetto, 
consulta [Il mio provider si aggiorna troppo spesso, cosa posso fare?](#il-mio-provider-si-aggiorna-troppo-spesso-cosa-posso-fare)
:::

### Come testare un oggetto che riceve [read] come parametro del suo costruttore?

Se stai usando il pattern descritto in [Posso leggere un provider senza ascoltarlo?](#posso-leggere-un-provider-senza-ascoltarlo), 
ti starai chiedendo come scrivere dei test per il tuo oggetto.

In questo scenario, considera testare il provider direttamente invece dell'oggetto puro.  
Puoi fare ciò usando la classe [ProviderContainer]:

```dart
final repositoryProvider = Provider((ref) => Repository(ref.read));

test('fetches catalog', () async {
  final container = ProviderContainer();
  addTearDown(container.dispose);

  Repository repository = container.read(repositoryProvider);

  await expectLater(
    repository.fetchCatalog(),
    completion(Catalog()),
  );
});
```

### Il mio provider si aggiorna troppo spesso, cosa posso fare?

Se il tuo oggetto è ricreato troppo spesso, è possibile che il tuo provider stia in ascolto 
di oggetti di cui non gli interessa.

Per esempio, potresti essere in ascolto di `Configuration` ma usare solo la proprietà `host`. 
Ascoltando l'intero oggetto `Configuration`, se un'altra proprietà che non sia `host` cambia, 
il tuo provider verrà ri-elaborato, il che potrebbe essere indesiderato.

La soluzione a questo problema è creare un provider separato che espone _solo_ ciò di cui 
hai bisogno di `Configuration` (quindi `host`):

**EVITARE** di stare in ascolto dell'intero oggetto:

```dart
final configsProvider = StreamProvider<Configuration>(...);

final productsProvider = FutureProvider<List<Product>>((ref) async {
  // productsProvider ri-otterrà i prodotti se qualsiasi cosa
  // nelle configurazioni cambia
  final configs = await ref.watch(configsProvider.future);

  return dio.get('${configs.host}/products');
});
```

**PREFERIRE** usare "select" quando necessiti solo di una singola proprietà dell'oggetto:

```dart
final configsProvider = StreamProvider<Configuration>(...);

final productsProvider = FutureProvider<List<Product>>((ref) async {
  // Ascolta solo la proprietà 'host'. Se qualcos'altro nelle configurazioni
  // cambia, non causerà inutili ri-elaborazioni del nostro provider.
  final host = await ref.watch(configProvider.selectAsync((config) => config.host));

  return dio.get('$host/products');
});
```

Questo ricostruirà `productsProvider` solo quando `host` cambierà.

[provider]: https://pub.dev/documentation/riverpod/latest/riverpod/Provider-class.html
[stateprovider]: https://pub.dev/documentation/riverpod/latest/riverpod/StateProvider-class.html
[futureprovider]: ../providers/future_provider
[statenotifierprovider]: ../providers/state_notifier_provider
[ref]: https://pub.dev/documentation/riverpod/latest/riverpod/Ref-class.html
[watch]: https://pub.dev/documentation/riverpod/latest/riverpod/Ref/watch.html
[read]: https://pub.dev/documentation/riverpod/latest/riverpod/Ref/read.html
[providercontainer.refresh]: https://pub.dev/documentation/riverpod/latest/riverpod/ProviderContainer/refresh.html
[ref.refresh]: https://pub.dev/documentation/flutter_riverpod/latest/flutter_riverpod/WidgetRef/refresh.html
[providercontainer]: https://pub.dev/documentation/riverpod/latest/riverpod/ProviderContainer-class.html
